import { CellView, EdgeView } from '@antv/x6';
import { EdgeOptions, NodeOptions, useGraphStore } from '@antv/xflow';

import { colors } from '@/config/theme';
import { useCurrentEdge } from '@/hooks/useCurrentEdge';
import { useGraph } from '@/hooks/useGraph';
import { useNodeResults } from '@/store/node-results';

export const GraphInit = memo<{ graphJson?: { nodes: NodeOptions[]; edges: EdgeOptions[] } }>((props) => {
  const initData = useGraphStore((state) => state.initData);
  const { graph, interacting, saveGraph } = useGraph();
  const { setActiveEdgeId } = useCurrentEdge();
  const { hasExecResult } = useNodeResults();

  useEffect(() => {
    setTimeout(() => {
      initData({ edges: [], nodes: [], ...((props.graphJson || {}) as any) });
      setTimeout(() => {
        graph?.centerContent();
      });
    }, 200);
  }, [graph, initData, props.graphJson]);

  
  graph?.disablePlugins(['button-remove']);

  const cellMouseenter = useCallback(
    (ev: CellView.MouseEventArgs<any>) => {
      const deleteHandle = () => {
        cell.remove();
        saveGraph();
      };

      if (hasExecResult) {
        return;
      }

      const cell = ev.cell;
      if (cell.isEdge()) {
        cell.addTools({
          name: 'button-remove',
          args: {
            distance: 0.8,
            markup: [
              {
                tagName: 'circle',
                selector: 'circle',
                attrs: {
                  r: 7,
                  fill: colors.red[500],
                  stroke: colors.red[500],
                  strokeWidth: 2,
                  cursor: 'pointer'
                }
              }
            ],
            onClick: deleteHandle
          }
        });
      } else if (cell.isNode()) {
        cell.addTools({
          name: 'button-remove',
          args: {
            x: '100%',
            y: 0,
            offset: { x: -2, y: 2 },
            onClick: deleteHandle
          }
        });
      }
    },
    [hasExecResult, saveGraph]
  );

  const cellMouseleave = useCallback((ev: CellView.MouseEventArgs<any>) => {
    const cell = ev.cell;
    cell.removeTools();
  }, []);

  const edgeClick = useCallback(
    (edge: EdgeView.PositionEventArgs<any>) => {
      setActiveEdgeId(edge.cell.id);
    },
    [setActiveEdgeId]
  );

  const edgeMouseenter = useCallback((edge: EdgeView.MouseEventArgs<any>) => {
    edge.cell.addTools({
      name: 'vertices',
      args: {
        attrs: { fill: '#8f8f8f' },
        modifiers: 'ctrl'
      }
    });

  }, []);

  const edgeMouseleave = useCallback((edge: EdgeView.MouseEventArgs<any>) => {
    if (edge.cell.hasTool('vertices')) {
      edge.cell.removeTool('vertices');
    }
  }, []);

  const saveChangedGraph = useCallback(() => {
    if (hasExecResult) {
      return;
    }
    saveGraph();
  }, [hasExecResult, saveGraph]);

  // mouseenter mouseleave
  useEffect(() => {
    const off = () => {
      if (graph) {
        graph.off('cell:mouseenter', cellMouseenter);
        graph.off('cell:mouseleave', cellMouseleave);
        graph.off('edge:mouseenter', edgeMouseenter);
        graph.off('edge:mouseleave', edgeMouseleave);
      }
    };
    if (graph) {
      if (interacting) {
        graph.on('cell:mouseenter', cellMouseenter);
        graph.on('cell:mouseleave', cellMouseleave);
        graph.on('edge:mouseenter', edgeMouseenter);
        graph.on('edge:mouseleave', edgeMouseleave);
      } else {
        off();
      }
    }
    return off;
  }, [cellMouseenter, cellMouseleave, edgeMouseenter, edgeMouseleave, graph, interacting]);

  useEffect(() => {
    if (graph) {
      if (interacting) {
        graph.on('edge:click', edgeClick);
        graph?.on('cell:change:*', saveChangedGraph);
      } else {
        graph.off('edge:click', edgeClick);
        graph?.off('cell:change:*', saveChangedGraph);
      }
    }

    return () => {
      if (graph) {
        graph.off('edge:click', edgeClick);
        graph?.off('cell:change:*', saveChangedGraph);
      }
    };
  }, [edgeClick, graph, saveChangedGraph, interacting]);

  return null;
});
